User Guide
Pre-Alpha Release
This version of JSxCAD is pre-alpha.
Some things are broken and some things will break.
You have been warned. :)
Main Site
Discussion Forum
Reponsitory
Introduction
The initial app can be opened via:
https://jsxcad.js.org/preAlpha
Appending #project will select a project (stored within local storage):
https://jsxcad.js.org/preAlpha#circle
Appending @gistUrl will load the initial script from a file named 'script' in the provided gist url:
https://jsxcad.js.org/preAlpha#circle@https://api.github.com/gists/3c39d513e91278681eed2eea27b0e589
Replace 3c39d513e91278681eed2eea27b0e589 in the example with your gist id.
Saving the project to gist is not yet supported.
Language
A small compiler is included which will transform input to a canonical form.
The basic api is implicitly imported into the top level scope, making 'circle', etc, available.
If no 'main' export is provided the compiler collects top level expressions other than exports, and bundles them into an implicit main function.
An implicit 'return' is placed before the last expression.
All functions are made async, and all function calls have an implicit await.
The intent is that it should be possible to write simple things easily, and have these translated into es6 modules automatically, while also allowing
es6 modules to be written out explicitly.
In some cases the analysis is confused by missing semicolons. If a script does not work, make sure all statements are terminated with semicolons.
Returning a shape from the script should produce a preview.
In order to generate stl, etc, use an operator like writeStl as described below. This should open a new window with a download button.
Operator Guide
Above
Moves the shape so that it is just above another shape (defaulting to the origin).
assemble(cube(10).above(),
cylinder(2, 15).rotateY(90))
cube(10).above(sphere(5))
Arc Cosine
Gives the arc cosine converted to degrees.
acos(a) => Math.acos(a) / (Math.PI * 2) * 360;
acos(0) = 90
acos(0.5) = 60
acos(1) = 0
Armature
Armature builds a set of points based on constraints.
const { angle, compute, distance, pinned } = armature();
angle('A', 'B', 'C', 90);
distance('B', 'A', 20);
distance('B', 'C', 10);
const { A, B, C } = compute();
polygon(A, B, C).center();
Assemble
Produces an assembly of shapes that can be manipulated as a single shape.
assemble(circle(20).translate([0, 0, -12]),
square(40).translate([0, 0, 16]).outline(),
cylinder(10, 20));
Components of the assembly can be extracted by tag filtering.
Components later in the assembly project holes into components earlier in the assembly so that the geometries are disjoint.
assemble(cube(30).above().as('cube'),
cylinder(10, 40).above().as('cylinder'))
assemble(cube(30).above().as('cube'),
cylinder(10, 40).above().as('cylinder'))
.keep('cube')
assemble(cube(30).above().as('cube'),
assemble(circle(40),
circle(50).outline()).as('circles'))
.keep('circles')
assemble(cube(30).above().as('cube'),
assemble(circle(40).as('circle'),
circle(50).outline().as('outline')))
.drop('outline')
As
Produces a version of a shape with user defined tags.
Back
Moves the shape so that it is just before the origin.
assemble(cylinder(2, 15).translate([0, 0, 2.5]),
cube(10).back())
Below
Moves the shape so that it is just below the origin.
assemble(cylinder(2, 15).rotateY(90),
cube(10).below())
cube(10).below(sphere(5))
Center
Moves the shape so that it is centered on the origin.
cube({ corner1: [30, -30, 10],
corner2: [10, -10, 0] })
cube({ corner1: [30, -30, 10],
corner2: [10, -10, 0] })
.center()
Chain Hull
Builds a convex hull between adjacent pairs in a sequence of shapes.
chainHull(cube(3).translate([-5, 5]),
sphere(3).translate([5, -5]),
cylinder(3, 10).translate([-10, -10]))
.translate([10, 10])
chainHull(circle(20).translate([0, 0, -10]),
circle(10),
circle(20).translate([0, 0, 10]))
Circle (disc)
Circles are approximated as surfaces delimeted by regular polygons.
Properly speaking what is produced here are discs.
The circle perimeter can be extracted via outline().
circle({ radius: 10,
sides: 8 })
circle({ apothem: 10,
sides: 8 })
assemble(circle({ apothem: 10, sides: 5 }),
circle({ radius: 10, sides: 5 }).drop(),
circle({ radius: 10 }).outline())
circle({ diameter: 20,
sides: 16 })
Color
Produces a version of a shape the given color.
Cosine
Gives the cosine in degrees.
cos(a) => Math.cos(a / 360 * Math.PI * 2);
cos(0) = 1
cos(45) = 0.707
cos(90) = 0
Cube (cuboid)
Generates cuboids.
cube({ corner1: [0, 0, 0],
corner2: [10, 10, 10] })
Cut
Cuts a shape into two halves at z = 0, and returns each.
const [top, bottom] = cube(10).cut();
assemble(top.translate(0, 0, 1),
bottom.translate(0, 0, -1));
const [top, bottom] = sphere(10).cut();
assemble(top.translate(0, 0, 2),
bottom.translate(0, 0, -2));
const [top, bottom] = sphere(10).rotateY(1).cut();
assemble(top.translate(0, 0, 2),
bottom.translate(0, 0, -2));
assemble(circle(10),
cylinder(5, 10))
.rotateY(90)
.cut()[0]
Cursor
A cursor is moved by transformations rather than the universe around it.
cursor()
.translate(5)
.turn(45)
.translate(5)
.interior()
cursor()
.translate(5)
.turn(-45)
.translate(5)
.interior()
cursor()
.translate(5)
.corner(45)
.translate(5)
.interior()
cursor()
.translate(5)
.corner(-45)
.translate(5)
.interior()
Cylinder
Generates cylinders.
cylinder({ radius: 2,
height: 10,
resolution: 8 })
cylinder({ apothem: 2,
height: 10,
resolution: 8 })
cylinder({ diameter: 6,
height: 8,
resolution: 16 })
Difference
Difference produces a version of the first shape with the remaining shapes removed, where applicable.
Different kinds of shapes do not interact. e.g., you cannot subtract a surface from a solid.
difference(cube(10).below(),
cube(5).below())
difference(circle(10),
circle(2.5))
difference(assemble(cube().below(),
cube().above()),
cube().right())
Drop from assembly
Generates an assembly from components in an assembly without a tag.
If no tag is supplied, the whole shape is dropped.
assemble(circle(10).as('A'),
square(10).as('B'))
assemble(circle(10).as('A'),
square(10).as('B'))
.drop('A')
assemble(circle(10).as('A'),
square(10).as('B'))
.drop('B')
assemble(circle(10).as('A'),
square(10).as('B'))
.drop('A', 'B')
assemble(cube(10).below(),
cube(8).below().drop())
Front
Moves the shape so that it is just before the origin.
assemble(cylinder(2, 15).translate([0, 0, 2.5]),
cube(10).front())
cube(10).front(sphere(5))
Fuse
Fuse produces a simple version of a shape. All substructure is discarded.
assemble(sphere(10), sphere(10).translate(2).drop()).fuse()
Extrude
Generates a solid from a surface.
difference(circle(10),
circle(8))
difference(circle(10),
circle(8))
.extrude({ height: 10 })
Hull
Builds the convex hull of a set of shapes.
hull(point([0, 0, 10]),
circle(10))
assemble(point([0, 0, 10]),
circle(10))
.hull()
point([0, 0, 10]).hull(circle(10))
hull(circle(4),
circle(2).translate(8));
Interior
Generates a surface from the interior of a simple closed path.
circle(10)
.outline()
.interior()
Intersection
Intersection produces a version of the first shape retaining only the parts included in the remaining shapes.
Different kinds of shapes do not interact. e.g., you cannot intersect a surface and a solid.
intersection(cube(12),
sphere(8))
intersection(circle(10).translate(-5),
circle(10).translate(5))
intersection(assemble(cube().below(),
cube().above()),
sphere(1))
assemble(difference(square(10),
square(7))
.translate(-2, -2),
difference(square(10),
square(7))
.translate(2, 2));
intersection(difference(square(10),
square(7))
.translate(-2, -2),
difference(square(10),
square(7))
.translate(2, 2));
Keep in assembly
Generates an assembly from components in an assembly with a tag.
assemble(circle(10).as('A'),
square(10).as('B'))
assemble(circle(10).as('A'),
square(10).as('B'))
.keep('A')
assemble(circle(10).as('A'),
square(10).as('B'))
.keep('B')
assemble(circle(10).as('A'),
square(10).as('B'))
.keep('A', 'B')
Left
Moves the shape so that it is just to the left of the origin.
assemble(cube(10).left(),
cylinder(2, 15))
Lego
FIX: Does not drop deep 'void'.
assemble(cube(8, 8, 3.2).above().as('plate'),
lego.socket().above().as('socket'))
Log
Writes a string to the console.
log("Hello, World")
Material
Produces a version of a shape with a given material.
Materials supported include 'paper', 'metal', 'glass'.
cylinder(5, 10).material('paper').color('pink')
cylinder(5, 10).material('metal').color('green')
cylinder(5, 10).material('glass').color('blue')
Max
Produces the maximum of a series of numbers.
max(1, 2, 3, 4) == 4
Measure Bounding Box
Provides the corners of the smallest orthogonal box containing the shape.
const [corner1, corner2] = sphere(7).measureBoundingBox();
cube({ corner1, corner2 })
Measure Center
Provides the center of the smallest orthogonal box containing the shape.
Micro Gear Motor
Minkowski (convex)
Generates the minkowski sum of a two convex shapes.
minkowski(cube(10),
sphere(3));
Numbers
numbers({ to: 10 }) is [0, 1, 2, 3, 4, 5, 6, 9].
numbers({ from: 3, to: 6 }) is [3, 4, 5, 6].
numbers({ from: 2, to: 8, by: 2 }) is [2, 4, 6].
Outline
Generates the outline of a surface.
difference(circle(10),
circle(2).translate([-4]),
circle(2).translate([4]))
difference(circle(10),
circle(2).translate([-4]),
circle(2).translate([4]))
.outline()
Plane
Generates a plane with the given constraints.
sphere(20).cut(planeX())[0];
sphere(20).cut(planeY(0))[0];
sphere(20).cut(planeZ(0))[1];
Point
Generates a point, by default at the origin.
Note: The points are not visible in the illustrations below.
Points
Generates point cloud.
Note: The points are not visible in the illustrations below.
points([ -0.5, -0.5, -0.5 ],
[ -0.5, -0.5, 0.5 ],
[ -0.5, 0.5, -0.5 ],
[ -0.5, 0.5, 0.5 ],
[ 0.5, -0.5, -0.5 ],
[ 0.5, -0.5, 0.5 ],
[ 0.5, 0.5, -0.5 ],
[ 0.5, 0.5, 0.5 ])
hull(points([ -0.5, -0.5, -0.5 ],
[ -0.5, -0.5, 0.5 ],
[ -0.5, 0.5, -0.5 ],
[ -0.5, 0.5, 0.5 ],
[ 0.5, -0.5, -0.5 ],
[ 0.5, -0.5, 0.5 ],
[ 0.5, 0.5, -0.5 ],
[ 0.5, 0.5, 0.5 ]))
Polygon
polygon([0, 1],
[1, 1],
[1, 0],
[0.2, 0.2])
polygon({ edge: 10, sides: 6 })
assemble(
polygon({ apothem: 10, sides: 5 }),
circle(10).drop())
assemble(
circle(10),
polygon({ radius: 10, sides: 5 }).drop())
polygon({ diameter: 20, sides: 3 })
Polyhedron
polyhedron({ points: [[10, 10, 0], [10, -10, 0], [-10, -10, 0], [-10, 10, 0], [0, 0, 10]],
triangles: [[4, 1, 0], [4, 2, 1], [4, 3, 2], [4, 0, 3], [3, 0, 1], [3, 1, 2]] })
Read Data Stitch Tajima
await readDst({ path: 'dst/atg-sft003.dst',
sources: [{ file: 'dst/atg-sft003.dst' },
{ url: 'https://jsxcad.js.org/dst/atg-sft003.dst' }] })
await readDst({ path: 'dst/atg-sft003.dst',
sources: [{ file: 'dst/atg-sft003.dst' },
{ url: 'https://jsxcad.js.org/dst/atg-sft003.dst' }] })
Read Font
readFont reads in a font and produces a function that renders text as a surface with that font.
The rendering function takes an option defaulting to { emSize = 10 } and a string of text.
This means that one M is 10 mm in height.
const greatVibes = await readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 20 }, "M").extrude(5).rotateX(90).above().center()
const greatVibes = await readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 10 }, "M").center()
const greatVibes = await readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 20 }, "M").center()
const greatVibes = await readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 16 }, "CA").center()
Read LDraw Parts
await readLDraw({ part: '3004.dat' })
Read Shape Geometry
This reads tagged geometry in json format and produces a shape.
await writeShape({ path: 'geometry/cube' }, cube())
await readShape({ path: 'geometry/cube' })
A shape building function can be supplied to generate the shape to read if absent.
The second read will not call the build function, and it will be present in re-runs.
This allows the caching of complex geometry for fast recomposition.
await readShape({ path: 'geometry/sphere' }, () => sphere())
await readShape({ path: 'geometry/sphere' }, () => sphere())
Read STL
await readStl({ path: 'stl/teapot.stl',
format: 'ascii',
sources: [{ file: 'stl/teapot.stl' },
{ url: 'https://jsxcad.js.org/stl/teapot.stl' }] })
Read Scalable Vector Format
const svg = await readSvg({ path: 'svg/butterfly.svg',
sources: [{ file: 'svg/butterfly.svg' },
{ url: 'https://jsxcad.js.org/svg/butterfly.svg' }] });
svg.center().scale(0.02)
Right
Moves the shape so that it is just to the right of the origin.
assemble(cube(10).right(),
cylinder(2, 15))
cube(10).right(sphere(5))
Rotate X
Rotates the shape around the X axis.
Rotate Y
Rotates the shape around the Y axis.
Rotate Z
Rotates the shape around the Z axis.
Scale
Scales an object uniformly or per axis.
Section
Produces a cross-section of a solid as a surface.
difference(cylinder(10, 10),
cylinder(8, 10))
difference(sphere(10),
sphere(8))
.section()
difference(sphere(10),
sphere(8))
.section()
.outline()
Sine
Gives the sine in degrees.
sin(a) => Math.sin(a / 360 * Math.PI * 2);
sin(0) = 0
sin(45) = 0.707
sin(90) = 1
Sphere
Generates spheres.
sphere({ radius: 8, resolution: 5 })
sphere({ diameter: 16, resolution: 64 })
Square Root
Gives the the square root of a number.
sqrt(a) => Math.sqrt(a);
sqrt(0) = 0
sqrt(4) = 2
sqrt(16) = 4
Square (rectangle)
Properly speaking what is produced here are rectangles.
assemble(circle(10),
square({ radius: 10 })
.drop())
assemble(square({ apothem: 10 }),
circle(10).drop())
Svg Path
Generates a path from svg path data.
svgPath({},
'M 120.25163,89.678938 C 105.26945,76.865343 86.290871,70.978848 64.320641,70.277872 z')
.center()
.scale(0.2)
Tetrahedron
Generates tetrahedrons.
tetrahedron({ radius: 8 })
tetrahedron({ diameter: 16 })
Torus
torus({ thickness: 5,
radius: 20 })
torus({ thickness: 5,
radius: 20,
sides: 4 })
torus({ thickness: 5,
radius: 20,
sides: 4,
rotation: 45 })
Translate
Translation moves a shape.
assemble(circle(),
sphere().above())
assemble(circle(),
sphere().above()
.translate(0, 0, 1))
assemble(circle(),
sphere().above()
.translate(0, 1, 0))
assemble(circle(),
sphere().above()
.translate([-1, -1, 1]))
Triangle
assemble(circle(10),
triangle({ radius: 10 })
.drop())
assemble(triangle({ apothem: 5 }),
circle(5).drop())
assemble(triangle({ radius: 10 })
.rotateZ(180),
triangle({ diameter: 10 })
.drop())
Union
Union produces a version of the first shape extended to cover the remaining shapes, as applicable.
Different kinds of shapes do not interact. e.g., you cannot union a surface and a solid.
union(sphere(5).left(),
sphere(5),
sphere(5).right())
union(sphere(5).left(),
sphere(5),
sphere(5).right())
.section()
.outline()
union(triangle(),
triangle().rotateZ(180))
union(triangle(),
triangle().rotateZ(180))
.outline()
union(assemble(cube().left(),
cube().right()),
cube().front())
.section()
.outline()
Wireframe
Generates a set of paths outlining a solid.
Write PDF
cube().section().writePdf('cube.pdf');
await writePdf({ path: 'cube.pdf' }, cube().section());
Write Shape Geometry
This writes a shape as a tagged geometry in json format.
await cube().writeShape('cube.shape');
await readShape({ path: 'cube.shape' })
await writeShape({ path: 'cube.shape' }, cube())
await readShape({ path: 'cube.shape' })
Write STL
await cube().writeStl('cube.stl');
await readStl({ path: 'cube.stl' });
await writeStl({ path: 'cube.stl' }, cube());
await readStl({ path: 'cube.stl' });
Write SVG
await cube().section().writeSvg('svg/cube1.svg');
await readSvg({ path: 'svg/cube1.svg' })
await writeSvg({ path: 'svg/cube2.svg' }, cube().section());
await readSvg({ path: 'svg/cube2.svg' })
Write SVG Photo
This takes a scene and a camera position and generates a two-dimensional SVG representation
as a svg tag.
Note: Illustrations broken due to scaling issue affecting readSvg.
await cube().writeSvgPhoto({ path: 'svg/cube3.svg', view: { position: [10, 10, 10], target: [0, 0, 0] } });
await readSvg({ path: 'svg/cube3.svg' })
await writeSvgPhoto({ path: 'svg/cube4.svg', view: { position: [10, 10, 10], target: [0, 0, 0] } }, cube());
await readSvg({ path: 'svg/cube4.svg' })